%include "boot.inc"
SECTION MBR vstart=0x7c00
    mov ax, cs ; cs=0, 把ax,ds,es,ss,fs置0
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov sp, 0x7c00  ; sp栈顶指针
    mov ax, 0xb800  ; 0xb8000-0xbffff 32KB 是实模式下文本模式显示内存
    mov gs, ax

    ; 利用INT 10, 功能号0x60: 上卷屏幕
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0
    mov dx, 184fh
    int 10h 
    
    ; 输出字符串"1 MBR"
    mov byte [gs:0x00], '1'
    mov byte [gs:0x01], 0xA4
    mov byte [gs:0x02], ' '
    mov byte [gs:0x03], 0xA4
    mov byte [gs:0x04], 'M'
    mov byte [gs:0x05], 0xA4
    mov byte [gs:0x06], 'B'
    mov byte [gs:0x07], 0xA4
    mov byte [gs:0x08], 'R'
    mov byte [gs:0x09], 0xA4

    ; 读扇区中的加载器
    mov eax, LOADER_START_SECTOR ; 加载器储存从第几个扇区开始, LBA扇区号
    mov bx, LOADER_BASE_ADDR ; 在实模式下，加载器将要被加载在1M主存位置, 写入地址
    mov cx, 4 ; 加载器占据了几个扇区， 读取扇区数
    call rd_disk_m_16 ; 存入下一条指令地址，sp指针移动

    jmp LOADER_BASE_ADDR + 0x300 ; loader的代码执行开始处距开始0x300

; 把储存在硬盘上加载器，加载到指定位置LOADER_BASE_ADDR 
rd_disk_m_16:
    mov esi, eax ; 备份eax
    mov di, cx ; 备份cx

; 读取硬盘7步
; 1. 设置要读取扇区数
    mov dx, 0x1f2 ; 启动磁盘通常都是第1通道primary通道下, 0x1f2端口，读写sector count寄存器
    mov al, cl ; 把要储存在cx寄存器中的读取扇区数，写入到ax寄存器中
    out dx, al ; 把al存储的sector count写入端口0x1f2，即写入sector count寄存器中

    mov eax, esi ; 恢复eax

; 2. 使用LBA28，将LBA地址低24位地址写入3个LBA寄存器中通过端口0x1f3, 0x1f4, 0x1f5. 
    mov dx, 0x1f3
    out dx, al

    mov cl, 8
    shr eax, cl
    mov dx, 0x1f4
    out dx, al

    shr eax, cl
    mov dx, 0x1f5
    out dx, al

; 3. 通过0x1f6端口，把24-27位写入device寄存器低4位，
; 第4位：指定主盘0和从盘1，第5,7位：固定1，第6位：LBA模式0和CHS模式1
   shr eax, cl 
   and al, 0x0f ; LBA扇区号24-27位，设置al低4位
   or al, 0xe0 ; 设置al高4位, 1110
   mov dx, 0x1f6
   out dx, al

; 4. 向0x1f7端口，写入读命令, 到command寄存器
    mov dx, 0x1f7
    mov al, 0x20 ; 0x20读操作
    out dx, al

; 5. 检测硬盘状态, 检测status寄存器
.not_ready:
    nop ; sleep一段时间，不打断之前的读操作. 因为都使用0x1f7端口
    in al, dx ; dx=0x1f7. 取出status寄存器中的值
    and al, 0x88 ; 0x88=1000 1000; 只保留第四位和第七位. 第4位为1表示已经数据准备好，第7位为1表示硬盘忙
    cmp al, 0x08 ; false说明第四位为0, 没有准备好
    jnz .not_ready

; 6. 硬盘准备好，可以从0x1f0端口读取数据了
    mov ax, di ; di要读取的扇区数
    mov dx, 256 ; data寄存器为2字节16bit, 一个扇区512字节. 读一个扇区256次
    mul dx ; ax*dx一共读多少次
    mov cx, ax
    mov dx, 0x1f0

.go_on_read:
    in ax, dx ; dx为0x1f0端口号，cx为循环次数 
    mov [bx], ax ; 把读取数据写到[bx]=LOADER_BASE_ADDR
    add bx, 2
    loop .go_on_read
    ret

    times 510-($-$$) db 0
    db 0x55, 0xaa